package cn.ppool.xmlmapper.worker;

import cn.ppool.xmlmapper.builder.MapperBuilder;
import cn.ppool.xmlmapper.builder.annotation.Entity;
import cn.ppool.xmlmapper.builder.parse.Mapper;
import cn.ppool.xmlmapper.builder.parse.model.base.BaseEntryBO;
import cn.ppool.xmlmapper.builder.parse.model.entry.*;
import cn.ppool.xmlmapper.builder.parse.worker.WorkStatusRecord;
import cn.ppool.xmlmapper.utils.MapBeanUtil;
import lombok.extern.log4j.Log4j2;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Log4j2
public class Worker implements InvocationHandler {

    private MapperBuilder configure;


    public void setConfigure(MapperBuilder configure) {
        this.configure = configure;
    }

    private Class<?> interfaceClass;

    public Object bind(Class<?> cls) {
        this.interfaceClass = cls;
        return Proxy.newProxyInstance(cls.getClassLoader(), new Class[]{interfaceClass}, this);
    }

    private List<Object> getParameterByClass(Map<String, Object> allVar, Class clazz) {
        return allVar.values().stream().filter(s -> s.getClass() == clazz).collect(Collectors.toList());
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        Map<String, Object> allVar = new HashMap<>();
        Map<String, Object> sqlVar = new HashMap<>();
        // 设置变量
        Parameter[] p = method.getParameters();
        for (int i = 0; i < p.length; i++) {
            Parameter pt = p[i];
            if (pt.getType() == Class.class) {
                continue;
            }
            Object var = args[i];
            String attrName = pt.getName();
            if (var == null) {          // null
                allVar.put(attrName, null);
            } else if (pt.getAnnotation(Entity.class) != null) {
                allVar.put(attrName, var);
            } else if (var instanceof String || var instanceof Number || var instanceof Boolean) {      // base type
                allVar.put(attrName, var);
            } else if (var instanceof List) {         // list
                allVar.put(attrName, var);
            } else if (var instanceof Map) {            // map
                allVar.putAll((Map) var);
            } else if (var instanceof Date) {
                allVar.put(attrName, var);
            } else {                // bean
                allVar.putAll(MapBeanUtil.objectToMap(var));
            }
        }
        //
        Mapper mapper = configure.getMapperList().get(method.getDeclaringClass().getName());
        BaseEntryBO bo = mapper.getEntry(method.getName());
        if (bo == null) {
            throw new RuntimeException("not found xml id:" + method.getName() + "请检查映射文件与id是否对上");
        }
        //
        WorkStatusRecord statusRecord = new WorkStatusRecord();
        bo.getWorkRecord().add(statusRecord);   // 立刻推到栈里面去
        statusRecord.setBeginQueryTime(new Date());
        statusRecord.setAllVal(allVar);
        statusRecord.setSqlVal(sqlVar);
        String sql = bo.complieEntity(allVar, sqlVar, new StringBuilder());
        if (sql != null) {
            sql = bo.cleanWhiteSpace(sql);
            statusRecord.setSql(sql);
        }
        //
        Object returnSuccess = null;
        try {
            if (bo instanceof SelectBO) {
                SelectBO selectBO = (SelectBO) bo;
                if (selectBO.getResultType() == Map.class ||
                        selectBO.getResultType() == String.class ||
                        selectBO.getResultType() == Integer.class ||
                        selectBO.getResultType() == Double.class ||
                        selectBO.getResultType() == Float.class ||
                        selectBO.getResultType() == Long.class ||
                        selectBO.getResultType() == Boolean.class) {
                    returnSuccess = configure.getSelectObjectPackerService().resultMapPacker(selectBO, sql, allVar, sqlVar);
                } else {
                    returnSuccess = configure.getSelectObjectPackerService().resultBeanPacker(selectBO, sql, allVar, sqlVar);
                }
            } else if (bo instanceof UpdateBO) {
                UpdateBO updateBO = (UpdateBO) bo;
                if (updateBO.getFunAnt() != null && updateBO.getFunAnt().entity().length > 0) {
                    List<Object> obj = getParameterByClass(allVar, updateBO.getFunAnt().entity()[0]);
                    if (obj.size() == 0) throw new RuntimeException("指定删除对象");
                    returnSuccess = configure.getRequestSqlService().requestUpdateEntity(updateBO.getFunAnt().entity()[0], obj.get(0));
                } else {
                    returnSuccess = configure.getRequestSqlService().requestUpdate(sql, allVar, sqlVar);
                }
            } else if (bo instanceof DeleteBO) {
                DeleteBO deleteBO = (DeleteBO) bo;
                if (deleteBO.getFunAnt() != null && deleteBO.getFunAnt().entity().length > 0) {
                    List<Object> obj = getParameterByClass(allVar, deleteBO.getFunAnt().entity()[0]);
                    if (obj.size() == 0) throw new RuntimeException("指定删除对象");
                    returnSuccess = configure.getRequestSqlService().requestDeleteEntity(deleteBO.getFunAnt().entity()[0], obj.get(0));
                } else {
                    returnSuccess = configure.getRequestSqlService().requestDelete(sql, allVar, sqlVar);
                }
            } else if (bo instanceof InsertBO) {
                InsertBO insertBO = (InsertBO) bo;
                if (insertBO.getFunAnt() != null && insertBO.getFunAnt().entity().length > 0) {
                    List<Object> obj = getParameterByClass(allVar, insertBO.getFunAnt().entity()[0]);
                    if (obj.size() == 0) throw new RuntimeException("指定插入对象");
                    returnSuccess = configure.getRequestSqlService().requestInsertEntity(insertBO.getFunAnt().entity()[0], obj.get(0));
                } else {
                    returnSuccess = configure.getRequestSqlService().requestInsert(sql, allVar, sqlVar);
                }
            } else if (bo instanceof FlushBO) {
                configure.getRequestSqlService().flush();
            } else {
                throw new RuntimeException("不支持的类型");
            }
        } catch (Exception e) {
            statusRecord.setError(e);
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException(e);
        } finally {
            statusRecord.setEndQueryTime(new Date());
        }
        return "void".equals(method.getReturnType().getName()) ? null : returnSuccess;
    }

}
